﻿namespace Whut.ObjectSharePointMapper.Internals
{
    using System;
    using System.ComponentModel;
    using Microsoft.SharePoint;
    using System.Collections.Generic;

    public static class TypeConverter
    {
        private static Type[] noConvertTypes = new Type[] { typeof(bool), typeof(int), typeof(double), typeof(string) };

        public static object ToEntityPropertyValue(Type propertyType, object sharePointValue)
        {
            if (Array.IndexOf(noConvertTypes, propertyType) >= 0)
            {
                return sharePointValue;
            }
            else if (propertyType.IsEnum)
            {
                return StringToEnum((string)sharePointValue, propertyType);
            }
            else if (propertyType == typeof(Lookup))
            {
                var lookup = new SPFieldLookupValue((string)sharePointValue);
                return new Lookup(lookup.LookupId, lookup.LookupValue);
            }
            else if (propertyType == typeof(MultiLookup))
            {
                //var lookup = (SPFieldLookupValueCollection)sharePointValue;
                var lookup = new SPFieldLookupValueCollection(sharePointValue.ToString());
                if (lookup == null)
                {
                    return null;
                }

                return new MultiLookup(lookup.ConvertAll<Lookup>(l => new Lookup(l.LookupId, l.LookupValue)));
            }
            else if (propertyType == typeof(DateTime))
            {
                return sharePointValue == null ? DateTime.MinValue : sharePointValue;
            }
            else
            {
                throw new ArgumentOutOfRangeException("propertyType", "Not supported entity property type.");
            }
        }

        public static object ToSPFieldValue(Type propertyType, object propertyValue)
        {
            if (Array.IndexOf(noConvertTypes, propertyType) >= 0)
            {
                return propertyValue;
            }
            else if (propertyType.IsEnum)
            {
                return EnumToString(propertyValue);
            }
            else if (propertyType == typeof(Lookup))
            {
                int lookupId = ((Lookup)propertyValue).Id;
                return (lookupId > 0 ? (object)lookupId : null);
            }
            else if (propertyType == typeof(MultiLookup))
            {
                var lookup = (MultiLookup)propertyValue;
                if (lookup.Lookups == null || lookup.Lookups.Count == 0)
                {
                    return null;
                }

                var lookups = new SPFieldLookupValueCollection();
                lookups.AddRange(lookup.Lookups.ConvertAll<SPFieldLookupValue>(l => new SPFieldLookupValue(l.Id, l.Value)));
                return lookups.ToString();
            }
            else if (propertyType == typeof(DateTime))
            {
                DateTime dateTime = (DateTime)propertyValue;
                return dateTime.Equals(DateTime.MinValue) ? null : propertyValue;
            }
            else
            {
                throw new ArgumentOutOfRangeException("propertyType", "Not supported entity property type.");
            }
        }

        // From http://blog.waynehartman.com/articles/84.aspx
        public static string EnumToString(object enumValue)
        {
            var fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
            DescriptionAttribute descriptionAttribute = fieldInfo.GetAttribute<DescriptionAttribute>();
            if (descriptionAttribute != null)
            {
                return descriptionAttribute.Description;
            }
            else
            {
                return enumValue.ToString();
            }
        }

        // From http://blog.waynehartman.com/articles/84.aspx
        public static object StringToEnum(string stringValue, Type enumType)
        {
            string[] names = Enum.GetNames(enumType);
            foreach (string name in names)
            {
                if (EnumToString(Enum.Parse(enumType, name)) == stringValue)
                {
                    return Enum.Parse(enumType, name);
                }
            }

            throw new ArgumentException("Not a description or value of the specified enum.", "stringValue");
        }
    }
}
